﻿#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.ScriptableObjects;
using static VRC.SDK3.Avatars.Components.VRCAvatarDescriptor;
using static VRC.SDK3.Avatars.Components.VRCAvatarDescriptor.CustomEyeLookSettings;
using static VRC.SDKBase.VRC_AvatarDescriptor;
namespace VRChatAvatarToolkit {
    public class FixAvatarToSdk3 : MoyuToolkitUtils {
        private static readonly string[] visemeBlendShapes = { "vrc.v_sil", "vrc.v_pp", "vrc.v_ff", "vrc.v_th", "vrc.v_dd", "vrc.v_kk", "vrc.v_ch", "vrc.v_ss", "vrc.v_nn", "vrc.v_rr", "vrc.v_aa", "vrc.v_e", "vrc.v_ih", "vrc.v_oh", "vrc.v_ou" };

        private Vector2 mainScrollPos;
        private GameObject lastAvatar;
        private AnimatorOverrideController standController, sitController;

        private void OnGUI()
        {
            mainScrollPos = GUILayout.BeginScrollView(mainScrollPos);
            GUILayout.Space(10);
            GUI.skin.label.fontSize = 24;
            GUI.skin.label.alignment = TextAnchor.MiddleCenter;
            GUILayout.Label("SDK升级工具");
            GUI.skin.label.fontSize = 12;
            GUI.skin.label.alignment = TextAnchor.MiddleCenter;
            GUILayout.Label("by:如梦");
            GUILayout.Space(10);
            GUI.skin.label.fontSize = 12;
            GUI.skin.label.alignment = TextAnchor.MiddleCenter;
            GUILayout.Label("支持SDK2模型转换至SDK3");
            GUILayout.Space(10);
            var nowAvatar = (GameObject)EditorGUILayout.ObjectField("选择模型：", lastAvatar, typeof(GameObject), true);

            var avatarType = GetAvatarType(nowAvatar);

            switch (avatarType)
            {
                case AvatarType.Null:
                    EditorGUILayout.HelpBox("请选择你的模型", MessageType.Warning);
                    standController = null;
                    sitController = null;
                    break;
                case AvatarType.FBX:
                    EditorGUILayout.HelpBox("当前模型是FBX格式，将使用默认参数进行转换", MessageType.Info);
                    break;
                case AvatarType.SDK3:
                    EditorGUILayout.HelpBox("已是SDK3模型了，不需要转换", MessageType.Error);
                    break;
                case AvatarType.Unknown:
                    EditorGUILayout.HelpBox("模型格式错误，仅支持人形Avatar", MessageType.Error);
                    break;
            }

            if (nowAvatar != lastAvatar)
            {
                lastAvatar = nowAvatar;
                standController = null;
                sitController = null;
                if (avatarType == AvatarType.SDK2)
                {
                    var descriptor = nowAvatar.GetComponent<VRCSDK2.VRC_AvatarDescriptor>();
                    standController = descriptor.CustomStandingAnims;
                    sitController = descriptor.CustomSittingAnims;
                }
            }

            standController = (AnimatorOverrideController)EditorGUILayout.ObjectField("Stand Controller：", standController, typeof(AnimatorOverrideController), true);
            sitController = (AnimatorOverrideController)EditorGUILayout.ObjectField("Sit Controller：", sitController, typeof(AnimatorOverrideController), true);

            GUILayout.Space(10);
            if ((avatarType == AvatarType.FBX || avatarType == AvatarType.SDK2) && GUILayout.Button("开始转换"))
            {
                GUI.enabled = false;
                ClearConsole();
                FixModel(nowAvatar, standController, sitController);
                GUI.enabled = true;
            }

            GUILayout.EndScrollView();
        }
        public static void FixModel(GameObject avatar_old, AnimatorOverrideController standController = null, AnimatorOverrideController sitController = null)
        {
            // 检查模型
            var avatarType = GetAvatarType(avatar_old);
            switch (avatarType)
            {
                case AvatarType.Null:
                    EditorUtility.DisplayDialog("提醒", "请先选择您的模型！", "确认");
                    return;
                case AvatarType.SDK3:
                    EditorUtility.DisplayDialog("提醒", "已是SDK3模型了，不需要转换。", "确认");
                    return;
                case AvatarType.Unknown:
                    EditorUtility.DisplayDialog("错误", "未知的模型格式！", "确认");
                    return;
            }
            var descriptor_2 = avatar_old.GetComponent<VRCSDK2.VRC_AvatarDescriptor>();
            if (avatarType == AvatarType.SDK2 && (standController == null || standController == null))
            {
                if (standController == null)
                    standController = descriptor_2.CustomStandingAnims;
                if (sitController == null)
                    sitController = descriptor_2.CustomSittingAnims;
            }


            // 克隆模型
            GameObject avatar_new;
            {
                var isPrefabFile = PrefabUtility.IsPartOfPrefabAsset(avatar_old);
                var oldAvatar = GameObject.Find(avatar_old.name + (isPrefabFile ? "" : "_sdk3"));
                if (oldAvatar)
                {
                    DestroyImmediate(oldAvatar);
                }
                avatar_new = Instantiate(avatar_old, avatar_old.transform.localPosition, Quaternion.identity);
                if (!isPrefabFile)
                    avatar_old.SetActive(false);
                avatar_new.SetActive(true);
                avatar_new.name = avatar_old.name + (isPrefabFile ? "" : "_sdk3");
            }

            // 获取旧AvatarDescriptor信息
            Print("读取AvatarDescriptor...");
            var oldDescriptor = avatar_new.GetComponent<VRCSDK2.VRC_AvatarDescriptor>();
            var hasSdk2Descriptor = oldDescriptor != null;
            {
                // 移除旧AvatarDescriptor
                Print("移除旧AvatarDescriptor...");
                if (hasSdk2Descriptor)
                {
                    DestroyImmediate(oldDescriptor);
                }
                else
                {
                    Print("模型中没有找到SDK2的VRC_AvatarDescriptor，将使用默认参数进行配置");
                }
            }

            // 添加新AvatarDescriptor
            Print("添加新AvatarDescriptor...");
            var descriptor = avatar_new.AddComponent<VRC.SDK3.Avatars.Components.VRCAvatarDescriptor>();
            var map = GetGameObjectForName(avatar_new, new string[] { "LeftEye", "RightEye", "Body" });

            Print("转移参数...");
            // ViewPosition
            if (hasSdk2Descriptor)
            {
                descriptor.ViewPosition = oldDescriptor.ViewPosition;
            }
            else
            {
                var lPos = map["LeftEye"].transform.position;
                descriptor.ViewPosition = new Vector3(0, lPos.y, Mathf.Abs(lPos.z * 1.2f));
            }

            // lipSync
            descriptor.lipSync = LipSyncStyle.VisemeBlendShape;
            descriptor.VisemeSkinnedMesh = map["Body"].GetComponent<SkinnedMeshRenderer>();
            descriptor.VisemeBlendShapes = visemeBlendShapes;

            // Eye Look
            if (map.ContainsKey("LeftEye") && map.ContainsKey("RightEye"))
            {
                descriptor.enableEyeLook = true;
                var customEyeLookSettings = descriptor.customEyeLookSettings;
                customEyeLookSettings.leftEye = map["LeftEye"];
                customEyeLookSettings.rightEye = map["RightEye"];

                customEyeLookSettings.eyesLookingUp = GetEyeRotations(-25, 0, 0);
                customEyeLookSettings.eyesLookingDown = GetEyeRotations(25, 0, 0);
                customEyeLookSettings.eyesLookingLeft = GetEyeRotations(0, -30, 0);
                customEyeLookSettings.eyesLookingRight = GetEyeRotations(0, 30, 0);
                /*
                customEyeLookSettings.eyelidType = EyelidType.Blendshapes;
                customEyeLookSettings.eyelidsSkinnedMesh = map["Body"].GetComponent<SkinnedMeshRenderer>();
                */
                descriptor.customEyeLookSettings = customEyeLookSettings;
            }


            // 分配模型ID，定义配置存放目录
            var avatarId = CreateAvatarId(avatar_new);
            var avatarPath = GetScriptPath() + "FixAvatarToSdk3/AvatarData/" + avatarId + "/";


            // 复制所需文件
            {
                Print("复制所需文件...");
                var defintePath = GetScriptPath() + "FixAvatarToSdk3/SDK3";

                CopyFolder(defintePath, avatarPath);
                Print("刷新资源缓存...");
                AssetDatabase.Refresh();
            }

            Print("配置PlayableLayers...");
            // 配置AvatarDescriptor PlayableLayers
            {
                descriptor.customizeAnimationLayers = true;
                var baseAnimationLayerType = new int[] { 0, 2, 3, 4, 5 };
                var baseAnimationLayers = new CustomAnimLayer[baseAnimationLayerType.Length];
                for (var i = 0; i < baseAnimationLayers.Length; i++)
                {
                    var type = (AnimLayerType)baseAnimationLayerType[i];
                    baseAnimationLayers[i].type = type;
                    string path;
                    switch (type)
                    {
                        case AnimLayerType.Base:
                            path = avatarPath + "Controller/Locomotion.controller";
                            break;
                        case AnimLayerType.Additive:
                            path = avatarPath + "Controller/Idle.controller";
                            break;
                        case AnimLayerType.Gesture:
                            path = avatarPath + "Controller/HandsLayer.controller";
                            break;
                        case AnimLayerType.Action:
                            path = avatarPath + "Controller/Action.controller";
                            break;
                        case AnimLayerType.FX:
                            path = avatarPath + "Controller/FXLayer.controller";
                            break;
                        default:
                            return;
                    }
                    var controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(path);
                    if (controller == null)
                    {
                        Debug.LogError("资源不存在：" + path);
                        baseAnimationLayers[i].isDefault = true;
                    }
                    else
                    {
                        baseAnimationLayers[i].animatorController = controller;
                        baseAnimationLayers[i].isDefault = false;
                    }
                }
                descriptor.baseAnimationLayers = baseAnimationLayers;
            }
            {
                var specialAnimationLayerType = new int[] { 6, 7, 8 };
                var specialAnimationLayers = new CustomAnimLayer[specialAnimationLayerType.Length];
                for (var i = 0; i < specialAnimationLayers.Length; i++)
                {
                    var type = (AnimLayerType)specialAnimationLayerType[i];
                    specialAnimationLayers[i].type = type;
                    string path = avatarPath + "Controller/" + type + ".controller";

                    var controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(path);
                    if (controller == null)
                    {
                        Debug.LogError("资源不存在：" + path);
                        specialAnimationLayers[i].isDefault = true;
                    }
                    else
                    {
                        specialAnimationLayers[i].animatorController = controller;
                        specialAnimationLayers[i].isDefault = false;
                    }
                }
                descriptor.specialAnimationLayers = specialAnimationLayers;
            }

            // 查找动作姿态
            //var clips = oldSitController.animationClips;
            var animMap = new Dictionary<string, AnimationClip>();
            var list = new List<KeyValuePair<AnimationClip, AnimationClip>>();

            if (standController)
            {
                standController.GetOverrides(list);
                foreach (var item in list)
                {
                    var key = item.Key.name;
                    var val = item.Value;
                    if (val != null)
                    {
                        animMap.Add(key, val);
                    }
                }
            }
            if (sitController)
            {
                sitController.GetOverrides(list);
                foreach (var item in list)
                {
                    var key = item.Key.name;
                    var val = item.Value;
                    if (key.Equals("IDLE") && val != null)
                        animMap.Add("SITTING", val);
                }
            }

            // Standing
            var standing = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.BlendTree>(avatarPath + "BlendTree/Standing.asset");
            var children = standing.children;
            if (animMap.ContainsKey("IDLE"))
                children[3].motion = animMap["IDLE"];
            standing.children = children;

            // Locomotion
            var locomotion = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(avatarPath + "Controller/Locomotion.controller");
            foreach (var layer in locomotion.layers)
            {
                if (layer.name.Equals("Locomotion"))
                {
                    foreach (var val in layer.stateMachine.states)
                    {
                        if (val.state.name.Equals("Standing"))
                        {
                            val.state.motion = standing;
                        }
                    }
                }
            }

            // HandsLayer
            var clipList = new List<string>();
            clipList.AddRange(new string[] { "Fist", "Open", "Point", "Peace", "RockNRoll", "Gun", "Thumbs up" });
            var clipKeys = new string[] { "FIST", "HANDOPEN", "FINGERPOINT", "VICTORY", "ROCKNROLL", "HANDGUN", "THUMBSUP" };
            var handsLayer = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(avatarPath + "Controller/HandsLayer.controller");
            foreach (var layer in handsLayer.layers)
            {
                if (layer.name.Equals("Left Hand") || layer.name.Equals("Right Hand"))
                {
                    foreach (var val in layer.stateMachine.states)
                    {
                        var index = clipList.IndexOf(val.state.name);
                        if (index >= 0 && animMap.ContainsKey(clipKeys[index]))
                        {
                            val.state.motion = animMap[clipKeys[index]];
                        }
                    }
                }
            }

            // FXLayer
            var fxLayer = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(avatarPath + "Controller/FXLayer.controller");
            foreach (var layer in fxLayer.layers)
            {
                if (layer.name.Equals("Left Hand Facial") || layer.name.Equals("Right Hand Facial"))
                {
                    foreach (var val in layer.stateMachine.states)
                    {
                        var index = clipList.IndexOf(val.state.name);
                        if (index >= 0 && animMap.ContainsKey(clipKeys[index]))
                        {
                            val.state.motion = animMap[clipKeys[index]];
                        }
                    }
                }
            }

            // Idle
            if (animMap.ContainsKey("IDLE"))
            {
                var idleLayer = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(avatarPath + "Controller/Idle.controller");
                foreach (var layer in idleLayer.layers)
                {
                    if (layer.name.Equals("Idle"))
                    {
                        foreach (var val in layer.stateMachine.states)
                        {
                            if (val.state.name.Equals("Upright Idle"))
                            {
                                val.state.motion = animMap["IDLE"];
                            }
                        }
                    }
                }
            }

            // Sitting
            if (animMap.ContainsKey("SITTING"))
            {
                var sittingLayer = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(avatarPath + "Controller/Sitting.controller");
                foreach (var layer in sittingLayer.layers)
                {
                    if (layer.name.Equals("Sitting"))
                    {
                        foreach (var val in layer.stateMachine.states)
                        {
                            var name = val.state.name;
                            if (name.Equals("WaitForSit") || name.Equals("SittingPose") || name.Equals("RestoreTracking"))
                            {
                                val.state.motion = animMap["SITTING"];
                            }
                        }
                    }
                }
            }

            // 配置AvatarDescriptor Expression
            Print("配置Expression...");
            var expressionsMenu = AssetDatabase.LoadAssetAtPath<VRCExpressionsMenu>(avatarPath + "Asset/Menu.asset");
            var expressionParameters = AssetDatabase.LoadAssetAtPath<VRCExpressionParameters>(avatarPath + "Asset/Parameters.asset");
            if (expressionsMenu != null && expressionParameters != null)
            {
                descriptor.customExpressions = true;
                descriptor.expressionsMenu = expressionsMenu;
                descriptor.expressionParameters = expressionParameters;
            }
            Print("转换完毕！");
            EditorUtility.DisplayDialog("提醒", "转换成功！\r\n新模型名字：" + avatar_new.name + "\r\n" +
                (hasSdk2Descriptor ? "请检查各项参数是否正常" : "请检查视角球等各项参数是否正常"), "确认");
            EditorGUIUtility.PingObject(avatar_new);
            Selection.activeGameObject = avatar_new;
        }

        private static EyeRotations GetEyeRotations(float x, float y, float z)
        {
            var eyesLooking = new EyeRotations();
            eyesLooking.left = eyesLooking.right = Quaternion.Euler(new Vector3(x, y, z));
            return eyesLooking;
        }

        public Texture2D TextureToTexture2D(Texture texture)
        {
            Texture2D texture2d = texture as Texture2D;
            TextureImporter ti = (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture2d));
            //图片Read/Write Enable的开关
            ti.isReadable = true;
            AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(texture2d));
            return texture2d;
        }

        public static void CopyFolder(string sourcePath, string destPath)
        {
            if (Directory.Exists(sourcePath))
            {
                if (!Directory.Exists(destPath))
                {
                    //目标目录不存在则创建
                    try
                    {
                        Directory.CreateDirectory(destPath);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("创建目标目录失败：" + ex.Message);
                    }
                }
                //获得源文件下所有文件
                List<string> files = new List<string>(Directory.GetFiles(sourcePath));
                files.ForEach(c =>
                {
                    if (!c.EndsWith(".meta"))
                    {
                        string destFile = Path.Combine(new string[] { destPath, Path.GetFileName(c) });
                        File.Copy(c, destFile, true);//覆盖模式
                    }
                });
                //获得源文件下所有目录文件
                List<string> folders = new List<string>(Directory.GetDirectories(sourcePath));
                folders.ForEach(c =>
                {
                    string destDir = Path.Combine(new string[] { destPath, Path.GetFileName(c) });
                    //采用递归的方法实现
                    CopyFolder(c, destDir);
                });
            }
            else
            {
                Debug.LogError("复制文件时找不到源文件：" + sourcePath);
            }
        }
    }
}
#endif